package com.example.apidocserver.domain.document.event.impl;

import com.alibaba.druid.util.StringUtils;
import com.example.apidocserver.application.dto.UserDocInfoDto;
import com.example.apidocserver.domain.document.dto.MenuApiNodeDto;
import com.example.apidocserver.domain.document.dto.UploadMenuApiDto;
import com.example.apidocserver.domain.document.entity.ApiInfo;
import com.example.apidocserver.domain.document.entity.ApiParam;
import com.example.apidocserver.domain.document.entity.ApiParamField;
import com.example.apidocserver.domain.document.entity.ApiParamHis;
import com.example.apidocserver.domain.document.event.ParamHisDomainEvent;
import com.example.apidocserver.domain.document.service.ApiParamHisService;
import com.example.apidocserver.domain.user.entity.DocUsrHis;
import com.example.apidocserver.infrastructure.constants.SystemConstants;
import com.example.apidocserver.infrastructure.util.ParamUtil;
import com.example.apidocserver.infrastructure.util.result.ParamException;
import com.example.apidocserver.infrastructure.util.user.UserDocAuthUtil;
import com.example.apidocserver.interfaces.vo.ApiInfoAllVo;
import com.example.apidocserver.interfaces.vo.ApiParamDescVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.example.apidocserver.infrastructure.constants.SystemConstants.*;

/**
 * 接口示例领域事件
 *
 * @author xyqlg
 */
@Service
@Slf4j
public class ParamHisDomainEventImpl implements ParamHisDomainEvent {
    @Resource
    private ApiParamHisService apiParamHisService;

    @Override
    public ApiInfoAllVo upsertParamHis(ApiParamHis apiParamHis, ApiInfo apiInfo) {
        ApiInfoAllVo apiInfoAllVo = new ApiInfoAllVo();
        Date now = new Date();
        ApiParamDescVo apiParamDescVo = paramHisToDescVo(apiParamHis, apiInfo, now, false);

        // 增加或修改参数示例
        apiParamHisService.insert(apiParamHis, now);
        apiInfoAllVo.setApiParamDescVo(apiParamDescVo).setApiInfo(apiInfo).setCurrentParamHis(apiParamHis);
        return apiInfoAllVo;
    }

    /**
     * 设置接口示例信息
     *
     * @param docUsrHis 用户信息
     * @param apiInfoAllVo   接口信息
     */
    @Override
    public void setParamHis(DocUsrHis docUsrHis, ApiInfoAllVo apiInfoAllVo) {
        if (Objects.isNull(apiInfoAllVo) || Objects.isNull(apiInfoAllVo.getApiInfo()) ||
                Objects.isNull(apiInfoAllVo.getApiInfo().getId())) {
            return;
        }

        // 1.设置查询条件，查询参数示例
        ApiParamHis apiParamHisQuery = new ApiParamHis().setApiId(apiInfoAllVo.getApiInfo().getId())
                .setEnvId(docUsrHis.getEnvId());
        UserDocInfoDto userDocInfoDto = UserDocAuthUtil.currentDocInfo();
        if (!userDocInfoDto.isWriteFlag()) {
            apiParamHisQuery.setEnableFlag(1);
        }
        List<ApiParamHis> apiParamHisList = apiParamHisService.queryByEntity(apiParamHisQuery);

        apiInfoAllVo.setApiParamHisList(apiParamHisList);
        if (apiParamHisList.isEmpty()) {
            // 设置默认参数示例，用于增加示例模板
            return;
        }

        // 2.设置最后访问参数示例
        apiParamHisList.sort(Comparator.comparing(apiParamHis -> Optional.ofNullable(apiParamHis.getOrderNo()).orElse(Integer.MIN_VALUE)));
        for (ApiParamHis apiParamHis : apiParamHisList) {
            if (apiParamHis.getId().equals(docUsrHis.getHisId())) {
                apiInfoAllVo.setCurrentParamHis(apiParamHis);
                if (Objects.isNull(docUsrHis.getEnvId())) {
                    docUsrHis.setEnvId(apiParamHis.getEnvId());
                }
                return;
            }
        }

        // 3.设置用户最后使用示例
        apiInfoAllVo.setCurrentParamHis(apiParamHisList.get(0));
        docUsrHis.setHisId(apiParamHisList.get(0).getId());
        if (Objects.isNull(docUsrHis.getEnvId())) {
            docUsrHis.setEnvId(apiParamHisList.get(0).getEnvId());
        }
    }

    @Override
    public List<ApiParamDescVo> insertBatchForInput(UploadMenuApiDto menuApiDto) {
        List<MenuApiNodeDto> menuNodeList = menuApiDto.getMenuNodeList();
        if (CollectionUtils.isEmpty(menuNodeList) || CollectionUtils.isEmpty(menuApiDto.getApiInfoList())
                || CollectionUtils.isEmpty(menuApiDto.getApiParamHisList())) {
            log.info("menuNode/apiInfo/apiParamHis is empty.");
            return Collections.emptyList();
        }
        // 1.查询已存在示例的接口，已存在示例的接口不增加示例
        Set<Integer> apiIdSet = menuApiDto.getApiInfoList().stream().map(ApiInfo::getId).collect(Collectors.toSet());
        List<Integer> apiIdDbList = this.apiParamHisService.findApiIdByApiIds(apiIdSet);

        // 2.转换为参数说明字段，设置参数排序值
        List<ApiParamDescVo> apiParamDescList = new ArrayList<>(menuApiDto.getApiParamHisList().size());
        List<ApiParamHis> apiParamHisNewList = new ArrayList<>(menuApiDto.getApiParamHisList().size());
        for (MenuApiNodeDto menuApiNodeDto : menuNodeList) {
            if (Objects.isNull(menuApiNodeDto.getApiParamHis()) || Objects.isNull(menuApiNodeDto.getApiInfo())
                    || apiIdDbList.contains(menuApiNodeDto.getApiInfo().getId())) {
                continue;
            }
            ApiInfo apiInfo = menuApiNodeDto.getApiInfo();
            ApiParamHis apiParamHis = menuApiNodeDto.getApiParamHis().setApiId(apiInfo.getId());
            apiParamHisNewList.add(apiParamHis);

            try {
                // 3.示例转参数说明对象
                apiParamDescList.add(paramHisToDescVo(apiParamHis, apiInfo, apiParamHis.getCreateTime(), false));
            } catch (ParamException paramException) {
                throw new ParamException("接口数据不规范，解析数据错误。接口名称为: " + apiInfo.getApiName() + "。错误信息："
                        + paramException.getMessage());
            }
        }

        this.apiParamHisService.insertBatch(apiParamHisNewList);
        return apiParamDescList;
    }

    private ApiParamDescVo paramHisToDescVo(ApiParamHis apiParamHis, ApiInfo apiInfo, Date now, boolean queryFlag) {
        ApiParamDescVo apiParamDescVo = new ApiParamDescVo();

        // 参数转换为参数说明，字段说明
        setInputHeader(apiParamHis, apiParamDescVo, now, apiInfo, queryFlag);
        setOutputHeader(apiParamHis, apiParamDescVo, now, apiInfo);
        setPath(apiParamHis, apiParamDescVo, now, apiInfo, queryFlag);
        setQuery(apiParamHis, apiParamDescVo, now, apiInfo, queryFlag);

        // 入参body参数
        setInputBody(apiParamHis, apiParamDescVo, now, apiInfo, queryFlag);

        setOutputBody(apiParamHis, apiParamDescVo, now, apiInfo);
        return apiParamDescVo;
    }

    private void setInputBody(ApiParamHis apiParamHis, ApiParamDescVo apiParamDescVo, Date now, ApiInfo apiInfo,
                              boolean queryFlag) {
        if (!StringUtils.isEmpty(apiParamHis.getParamInputBodyStr()) &&
                !queryFlag && apiParamHisService.isNewInputBody(apiParamHis.getParamInputBodyStr(), apiInfo)) {
            // 1.字段从转参数说明
            Map<String, ApiParam> inputBodyMap = ParamUtil.getBodyParam(apiParamHis.getParamInputBodyStr(), now, apiInfo);

            ArrayList<ApiParam> inputBodyList = new ArrayList<>(inputBodyMap.size());
            inputBodyMap.forEach((key, apiParam) -> {
                apiParam.setDataType(SystemConstants.DT_INPUT).setParamType(PT_BODY).setApiId(apiInfo.getId());
                inputBodyList.add(apiParam);
            });
            apiParamDescVo.setInputBody(inputBodyList);
        } else {
            apiParamDescVo.setInputBody(Collections.emptyList());
        }
    }

    private void setOutputBody(ApiParamHis apiParamHis, ApiParamDescVo apiParamDescVo, Date now, ApiInfo apiInfo) {
        if (!StringUtils.isEmpty(apiParamHis.getParamOutputBodyStr()) &&
                apiParamHisService.isNewOutputBody(apiParamHis.getParamOutputBodyStr(), apiInfo)) {
            // 如果是新增字段，这新增参数说明
            Map<String, ApiParam> outputBodyMap = ParamUtil.getBodyParam(apiParamHis.getParamOutputBodyStr(), now, apiInfo);
            ArrayList<ApiParam> outputBodyList = new ArrayList<>(outputBodyMap.size());
            outputBodyMap.forEach((key, apiParam) -> {
                apiParam.setDataType(SystemConstants.DT_OUTPUT).setParamType(PT_BODY).setApiId(apiInfo.getId());
                outputBodyList.add(apiParam);
            });
            apiParamDescVo.setOutputBody(outputBodyList);
        } else {
            apiParamDescVo.setOutputBody(Collections.emptyList());
        }
    }

    private void setInputHeader(ApiParamHis apiParamHis, ApiParamDescVo apiParamDescVo, Date now, ApiInfo apiInfo,
                                boolean queryFlag) {
        if (!StringUtils.isEmpty(apiParamHis.getParamInputHeaderStr())) {
            // 1.查询数据库是否以存在相同参数
            List<ApiParamHis> inputHeaderList = queryFlag ? apiParamHisService.queryByApiIdAndInputHeader(apiParamHis.getApiId(),
                    apiParamHis.getParamInputHeaderStr()) : Collections.emptyList();

            if (CollectionUtils.isEmpty(inputHeaderList)) {
                // 2.转换为字段说明
                List<ApiParamField> apiParamFields = ParamUtil.changeHeaderField(apiParamHis.getParamInputHeaderStr(),
                        now, apiInfo);

                // 3.创建参数说明对象
                ApiParam apiParam = ParamUtil.getApiParam(apiParamFields, apiInfo, PT_HEADER);
                apiParamDescVo.setInputHeader(apiParam);
            }
        }
    }


    private void setPath(ApiParamHis apiParamHis, ApiParamDescVo apiParamDescVo, Date now, ApiInfo apiInfo,
                         boolean queryFlag) {
        if (!StringUtils.isEmpty(apiParamHis.getParamInputPathStr())) {
            List<String> inputPathList = queryFlag ? apiParamHisService.queryByApiId(apiParamHis.getApiId())
                    : Collections.emptyList();

            List<ApiParamField> apiParamFields = ParamUtil.changeInputPathField(apiParamHis.getParamInputPathStr(), now, apiInfo);
            ApiParam apiParam = ParamUtil.getApiParam(apiParamFields, apiInfo, PT_PATH);
            apiParamDescVo.setInputPath(apiParam);

            if (!CollectionUtils.isEmpty(inputPathList)) {
                int maxLength = inputPathList.stream().map(str -> str.split(PATH_BASE_STR))
                        .mapToInt(strings -> strings.length).max().orElse(0);
                String[] split = apiParamHis.getParamInputPathStr().split(PATH_BASE_STR);
                if (split.length > maxLength) {
                    apiParam.setFields(apiParamFields.subList(split.length - maxLength, apiParamFields.size()));
                } else {
                    apiParam.setFields(Collections.emptyList());
                }
            }
        }
    }

    private void setQuery(ApiParamHis apiParamHis, ApiParamDescVo apiParamDescVo, Date now, ApiInfo apiInfo,
                          boolean queryFlag) {
        if (!StringUtils.isEmpty(apiParamHis.getParamInputQueryStr())) {
            // 1.查询相同字符串
            List<ApiParamHis> inputQueryList = queryFlag ?
                    apiParamHisService.queryByApiIdAndInputQuery(apiParamHis.getApiId(), apiParamHis.getParamInputQueryStr())
                    : Collections.emptyList();

            // 2.没有相同字符串则转换字段
            if (CollectionUtils.isEmpty(inputQueryList)) {
                List<ApiParamField> apiParamFields = ParamUtil.changeInputQueryField(apiParamHis.getParamInputQueryStr(),
                        now, apiInfo);
                ApiParam apiParam = ParamUtil.getApiParam(apiParamFields, apiInfo, PT_QUERY);
                apiParamDescVo.setInputQuery(apiParam);
            }
        }
    }

    private void setOutputHeader(ApiParamHis apiParamHis, ApiParamDescVo apiParamDescVo, Date now, ApiInfo apiInfo) {
        if (!StringUtils.isEmpty(apiParamHis.getParamOutputHeaderStr())) {
            // 1.查询相同字符串
            List<ApiParamHis> outputHeaderList =
                    apiParamHisService.queryByApiIdAndOutputHeader(apiParamHis.getApiId(), apiParamHis.getParamOutputHeaderStr());

            // 2.没有相同字符串则转换字段
            if (CollectionUtils.isEmpty(outputHeaderList)) {
                List<ApiParamField> apiParamFields = ParamUtil.changeHeaderField(apiParamHis.getParamOutputHeaderStr(),
                        now, apiInfo);
                ApiParam apiParam = ParamUtil.getApiParam(apiParamFields, apiInfo, PT_HEADER).setDataType(DT_OUTPUT);
                apiParamDescVo.setOutputHeader(apiParam);
            }
        }
    }
}
